#include "MainWindow.h"
#include "ui_MainWindow.h"
#include <QImage>
#include <QMessageBox>
#include <QDebug>
#include <QFileDialog>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),    
    ui(new Ui::MainWindow),
    file(""),
    p_Original_out(NULL)
{
    ui->setupUi(this);

    connect(this->ui->actionOpen, SIGNAL(triggered()), SLOT(on_action_openFile()));
    connect(this->ui->label_preview, SIGNAL(mousePressed(QPoint)), this, SLOT(on_mouseDown(QPoint)));
    connect(this->ui->label_preview, SIGNAL(mouseReleased(QPoint)), this, SLOT(on_mouseUp(QPoint)));

    readSettrings();
}

MainWindow::~MainWindow()
{
    writeSettings();

    delete ui;

    if(p_Original_out != NULL)
        cvReleaseImage(&p_Original_out);
}

static QImage IplImage2QImage(const IplImage *iplImage)
/*
 * Вспомогательная функция c Nokia.developers, для преобразования изображения openCV в QImage
 */
{
    int height = iplImage->height;
    int width = iplImage->width;

    if  (iplImage->depth == IPL_DEPTH_8U && iplImage->nChannels == 3)
    {
      const uchar *qImageBuffer = (const uchar*)iplImage->imageData;
      QImage img(qImageBuffer, width, height, QImage::Format_RGB888);
      return img.rgbSwapped();
    } else if  (iplImage->depth == IPL_DEPTH_8U && iplImage->nChannels == 1){
    const uchar *qImageBuffer = (const uchar*)iplImage->imageData;
    QImage img(qImageBuffer, width, height, QImage::Format_Indexed8);

    QVector<QRgb> colorTable;
    for (int i = 0; i < 256; i++){
        colorTable.push_back(qRgb(i, i, i));
    }
    img.setColorTable(colorTable);
    return img;
    }else{
      qWarning() << "Image cannot be converted.";
      return QImage();
    }
}

void MainWindow::on_pushButton_clicked()
{
    if(file != "")
    {
        IplImage *img = cvLoadImage(file.toLocal8Bit().data(), CV_LOAD_IMAGE_COLOR);
        IplImage* gray, * binary;

        // создаём одноканальные картинки
        gray = cvCreateImage( cvGetSize(img), IPL_DEPTH_8U, 1 );
        binary = cvCreateImage( cvGetSize(img), IPL_DEPTH_8U, 1 );

        if(p_Original_out != NULL)
            cvReleaseImage(&p_Original_out);
        // клонируем
        IplImage* dst = cvCloneImage(img);

        // преобразуем в градации серого
        cvCvtColor(img, gray, CV_RGB2GRAY);

        // преобразуем в двоичное
        cvInRangeS(gray, cvScalar(40), cvScalar(150), binary);

        CvMemStorage* storage = cvCreateMemStorage(0);
        CvSeq* contours=0;

        // находим контуры
        int contoursCont = cvFindContours( binary, storage, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_NONE, cvPoint(0,0));
        QString s; s.sprintf("%d contorous", contoursCont);
        ui->label_3->setText(s);

        cvCvtColor( binary, dst, CV_GRAY2BGR );

        // нарисуем контуры
        for(CvSeq* seq0 = contours;seq0!=0;seq0 = seq0->h_next){
                cvDrawContours(dst, seq0, CV_RGB(255,0,0), CV_RGB(0,255,0), 0, 1, 8); // рисуем контур
        }

        // сохранение изображения контуров
        int index = file.lastIndexOf(".jpg");
        QString file_out = file;
        file_out.insert(index, "_contours");
        IplImage* small;

        // пропорциональное сжатие изображения для вывода превью на форме
        CvSize size;

        int w = ui->label_preview->width(), h = ui->label_preview->height();
        if(dst->width > w || dst->height > h){
            float k;
            float kx = (float)h / dst->height;
            float ky = (float)w / dst->width;

            if(ky < kx){
                if(ky < 1){ k = ky;}
            }
            else{
                if(kx < 1){ k = kx;}
            }

            size.height = dst->height*k - 2; // -2 из-за внутренних контуров на qlabel, иначе будет раздвигать его
            size.width = dst->width*k - 2;

            small = cvCreateImage( size, dst->depth, dst->nChannels );
            cvResize(dst, small);

            scale_coef = k;
        }
        else{
            // не требуется сжатия, выводим как есть
            size = {dst->width, dst->height};
            small = dst;
            scale_coef = 1;
        }


        // сохраняем в файл
        cvSaveImage(file_out.toLocal8Bit().data(), dst);

        // выводим превью на форму
        QImage preview;

        preview = IplImage2QImage(small);

        if(preview.isNull() == true){
            QMessageBox mb;
            mb.exec();
        }
        else ui->label_preview->setPixmap(QPixmap::fromImage(preview));

        p_Original_out = dst;
        scaled_size = size;

        // очищаем память
        if(dst != small) cvReleaseImage(&small);
        cvReleaseImage(&img);
        cvReleaseImage(&gray);        
        cvReleaseImage(&binary);
        cvReleaseMemStorage(&storage);
    }
    else
    {
        QMessageBox mb(QMessageBox::Warning, "Error", "No one file is open!", QMessageBox::Ok );
        mb.exec();
    }
}

void MainWindow::on_action_openFile()
{   // обработчик события меню "открыть файл"
    QFileDialog dg;
    dg.setNameFilter(QString("*.jpg"));
    QString fileName = dg.getOpenFileName(0, 0, "", "Image Files (*.png *.jpg *.bmp)");
    if(fileName != "")
    {
        file = fileName;
    }
}

void MainWindow::readSettrings()
{   // чтение настоек приложения из реестра
    m_settings.beginGroup("/Settings");

    file = m_settings.value("/File").toString();

    m_settings.endGroup();
}

void MainWindow::writeSettings()
{   // сохранение настоек приложения в реестр
    m_settings.beginGroup("/Settings");

    m_settings.setValue("/File", file);

    m_settings.endGroup();
}

void MainWindow::on_mouseDown(QPoint p)
{
    mousePos[0] = p;

}

void MainWindow::on_mouseUp(QPoint p)
{
    if(p_Original_out == NULL) return; // если изображение не открыто - выход

    QString debug_info; // строка дебага в диалог образца

    // приведение позиции к образцу(т.к. картинка рис. в центре обл.)

    CvSize offset ={(ui->label_preview->width() - scaled_size.width)/2,
                   (ui->label_preview->height() - scaled_size.height)/2};

    ui->label_preview->height();

    // mousePos[0]/k  == start position of area into original image
    mousePos[1] = p;
    int rect_width = abs(mousePos[0].x() -  mousePos[1].x());
    int rect_height = abs(mousePos[0].y() -  mousePos[1].y());
    debug_info.sprintf("selected area:\nheight = %d, width = %d\n",
                       rect_height, rect_width);

    if(rect_height > 0 && rect_width > 0)
    {
        cvSetImageROI(  //  привести область(ROI) к размерам на оригинале
            p_Original_out,
            cvRect(
                (MIN(mousePos[0].x(), mousePos[1].x()) - offset.width)/scale_coef,
                (MIN(mousePos[0].y(), mousePos[1].y()) - offset.height)/scale_coef,
                rect_width/scale_coef,
                rect_height/scale_coef)
        );

        CvSize size;
        size.height = rect_height/scale_coef;
        size.width = rect_width/scale_coef;

        QString tmp;
        tmp.sprintf("projection of area to original:\nheight = %d, width = %d\n", size.height, size.width);
        debug_info += tmp;

        IplImage* small = cvCreateImage( size, p_Original_out->depth, p_Original_out->nChannels );
        cvResize(p_Original_out, small); //  - вырезать с оригинала
        cvResetImageROI(p_Original_out); // Сбрасываем ROI

        // смасштаблировать в размер для образцов
        IplImage* sample;
        int w = 140, h = 140;
        if(small->width > w || small->height > h)
        {
            float k;
            float kx = (float)h / small->height;
            float ky = (float)w / small->width;

            if(ky < kx){
                if(ky < 1){ k = ky;}
            }
            else{
                if(kx < 1){ k = kx;}
            }

            size.height = small->height*k - 2; // -2 из-за внутренних контуров на qlabel, иначе будет раздвигать его
            size.width = small->width*k - 2;

            sample = cvCreateImage( size, small->depth, small->nChannels );
            cvResize(small, sample);

            QString tmp;
            tmp.sprintf("prev. scaling coef. = %f", k);
            debug_info += tmp;
        }
        else{
            // не требуется сжатия, выводим как есть
            sample = small;
            debug_info += " without scaling sample_prev.\n";
        }

        // закинуть изображение в диалог образца
        QImage samplePreview = IplImage2QImage(sample);
        m_sampleDialog.setImage(samplePreview);
        m_sampleDialog.setDebugInfo(debug_info);
        m_sampleDialog.exec();

        // очистка ресурсов
        if(sample != small) cvReleaseImage(&sample);
        cvReleaseImage(&small);
    }
    else
    {
        // ошибка если выделение мало
        QMessageBox mb(QMessageBox::Warning, "Error",
            "Selected area for sample must be greater than zero!",
            QMessageBox::Ok );
        mb.exec();
    }
}
